home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Snippets / digitalaudio / digitalaudio.c < prev   
Encoding:
C/C++ Source or Header  |  1994-04-21  |  29.6 KB  |  1,214 lines  |  [TEXT/R*ch]

  1. /* ----------------------------------------------------------------- */
  2. /* ReadAudioFromCD.c */
  3.  
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <stdarg.h>
  8. #include <Sound.h>
  9. #include <Sane.h>
  10. #include <Script.h>
  11. #include <Files.h>
  12. #include <StandardFile.h>
  13.  
  14. enum csCDCode
  15. {
  16.     csEjectTheDisc = 7,
  17.     csDiscStatus = 8,
  18.     csGetDriveType = 96,
  19.     csWhoIsThere = 97,
  20.     csReadTOC = 100,
  21.     csReadQ = 101,
  22.     csReadHeader = 102,
  23.     csReadMCN = 110,
  24.     csReadISRC = 111,
  25.     csReadAudio = 115
  26. };
  27.  
  28. enum csReadTOCSubCodes
  29. {
  30.     csReadTOCTrackCount = 1,
  31.     csReadTOCLeadOut = 2,
  32.     csReadTOCStartingAddress = 3,
  33.     csReadTOCTableOfContents = 4,
  34.     csReadTOCNrOfSessions = 5,
  35.     csReadTOCQSubCodes = 6
  36. };
  37.  
  38. enum csReadAudioCodes
  39. {
  40.     csReadAudioDataOnly = 0, /* 2352 bytes per block */
  41.     csReadAudioWithQSubCode = 1, /* 2368 bytes per block */
  42.     csReadAudioWithAllSubCodes = 2, /* 2448 bytes per block */
  43.     csReadAudioSubCodesOnly = 3 /* 96 bytes per block */
  44. };
  45.  
  46. enum AddressTypes
  47. {
  48.     logicalBlockAddressType = 0,
  49.     minSecFrameType = 1,
  50.     bcdTrackNumberType = 2
  51. };
  52.  
  53. enum DriveTypes
  54. {
  55.     AppleCDSC = 1,
  56.     AppleCDSCPlus150 = 2,
  57.     AppleCD300 = 3
  58. };
  59.  
  60. typedef struct StartingAddress
  61. {
  62.     Byte controlField;
  63.     Byte minutes; /* BCD */
  64.     Byte seconds; /* BCD */
  65.     Byte frames; /* BCD */
  66. } *StartingAddress;
  67.  
  68. typedef struct
  69. {
  70.     ParamBlockHeader
  71.     short int ioRefNum;
  72.     short int csCode;
  73.     union
  74.     {
  75.     struct
  76.     {
  77.         short int track;
  78.         Byte writeProtect;
  79.         Byte discStatus;
  80.         Byte installed;
  81.         Byte side;
  82.         long int qLink;
  83.         short int qType;
  84.         short int dQDrive;
  85.         short int dQRefNum;
  86.         short int dQFSID;
  87.         Byte twoSideFormat;
  88.         Byte needsFlush;
  89.         Byte diskErrs;
  90.     } discStatus;
  91.     struct
  92.     {
  93.         Byte fill;
  94.         Byte SCSIMask;
  95.     } showIsThere;
  96.     union
  97.     {
  98.         short int subType;
  99.         struct /* csReadTOCTrackCount */
  100.         {
  101.         Byte firstTrackNumber; /* BCD */
  102.         Byte lastTrackNumber; /* BCD */
  103.         } trackCount;
  104.         struct /* csReadTOCLeadOut */
  105.         {
  106.         Byte minutes; /* BCD */
  107.         Byte seconds; /* BCD */
  108.         Byte frames; /* BCD */
  109.         } leadOut;
  110.         struct /* csReadTOCStartingAddress */
  111.         {
  112.         short int subType;
  113.         StartingAddress buffer;
  114.         short int bufferSize;
  115.         Byte startingTrackNumber; /* BCD */
  116.         Byte fill;
  117.         } startingAddress;
  118.         struct /* csReadTOCTableOfContents */
  119.         {
  120.         short int subType;
  121.         void *buffer;
  122.         } tableOfContents;
  123.         struct /* csReadTOCNrOfSessions */
  124.         {
  125.         short int subType;
  126.         short int firstSessionNumber; /* BCD */
  127.         short int lastSessionNumber; /* BCD */
  128.         struct
  129.         {
  130.             Byte controlField;
  131.             Byte minutes; /* BCD */
  132.             Byte seconds; /* BCD */
  133.             Byte frames; /* BCD */
  134.         } firstTrackOfLastSession;
  135.         } nrOfSessions;
  136.         struct /* csReadTOCQSubCodes */
  137.         {
  138.         short int subType;
  139.         void *buffer;
  140.         short int bufferSize;
  141.         Byte startingSessionNumber; /* BCD */
  142.         Byte fill;
  143.         } qSubCode;
  144.     } readTOC;
  145.     struct
  146.     {
  147.         Byte mcValFound; /* MCVal found iff (mcValFound & 0x80) == 0x80 */
  148.         Byte mcn[15]; /* Media Catalog Number from MSB to LSB */
  149.     } readMCN;
  150.     struct
  151.     {
  152.         Byte tcValFound; /* ISRC data found iff (tcValFound & 0x80) == 0x80 */
  153.         Byte isrc[15]; /* Media Catalog Number from MSB to LSB */
  154.     } readISRC;
  155.     struct
  156.     {
  157.         short int audioType;
  158.         short int *buffer;
  159.         short int addressType;
  160.         union
  161.         {
  162.         long int logicalBlockAddress;
  163.         struct
  164.         {
  165.             Byte controlField;
  166.             Byte minutes; /* BCD */
  167.             Byte seconds; /* BCD */
  168.             Byte frames; /* BCD */
  169.         } startTime;
  170.         Byte trackNumber; /* BCD */
  171.         } address;
  172.         int numberOfFrames;
  173.         short int filler[4];
  174.     } readAudio;
  175.     short int driveType;
  176.     short int csParam[11];
  177.     } csParam;
  178. } CDIORec;
  179.  
  180. typedef union IntelWord
  181. {
  182.     Byte lh[2];
  183.     short int word;
  184. } IWrd;
  185.  
  186. static int nrTracks;
  187. static struct StartingAddress tracks[100];
  188. static int scsiID;
  189. static int driverRefNum;
  190. static int ioRefNum;
  191. static Handle sndHandle = NULL;
  192. static long int bufSize = 0;
  193. static int startTrack, startMinutes, startSeconds, startFrames,
  194.        endTrack,   endMinutes,   endSeconds,   endFrames;
  195. static OSErr mcnErr, isrcErr;
  196. static Boolean discPresent;
  197. static struct StartingAddress totalTime;
  198. static Byte mcn[15], isrc[15];
  199. static enum DriveTypes driveType;
  200. static unsigned int channels = 1, resolution = 8, sampleRate = 11025; 
  201. struct SndStruct
  202. {
  203.     unsigned int resource; /* Should be 1 */
  204.     struct SynthDef
  205.     {
  206.     short int nrOfSynths;
  207.     struct
  208.     {
  209.         short int resourceID;
  210.         long int initCmd;
  211.     } synth[1];
  212.     } synthDef;
  213.     unsigned int nrOfSoundCommands; /* Should be 1 */
  214.     struct SndCommand soundCommand;
  215.     Ptr data;
  216.     unsigned long int nrOfSamples; /* Or numChannels! */
  217.     Fixed sampleRate;
  218.     unsigned long int loopStart;
  219.     unsigned long int loopEnd;
  220.     unsigned char encode;
  221.     unsigned char baseNote;
  222.     struct ExtHeader
  223.     {
  224.     unsigned long numFrames;
  225.     extended80 AIFFSampleRate;
  226.     Ptr markerChunk;
  227.     Ptr instrumentChunks;
  228.     Ptr AESRecording;
  229.     unsigned short sampleSize;
  230.     unsigned short futureUse1;
  231.     unsigned long futureUse2;
  232.     unsigned long futureUse3;
  233.     unsigned long futureUse4;
  234.     } extHeader;
  235. } genHeader = {1, {1, {{sampledSynth, initMono}}},
  236.            1, {dataOffsetFlag | bufferCmd, 0, 0x14},
  237.            NULL, 0, 0x56EE8BA3, 0, 0, stdSH, 60};
  238. static int headerSize;
  239. static long int numFrames;
  240. static Boolean normalize = false;
  241.  
  242. static void aprintf(char *format, ...)
  243. {
  244.     va_list args;
  245.     char pText[256];
  246.  
  247.     va_start(args, format);
  248.     pText[0] = vsprintf(pText + 1, format, args);
  249.     va_end(args);
  250.     ParamText(pText, "\p", "\p", "\p");
  251.     Alert(128, NULL);
  252. }
  253.  
  254. static void dprintf(DialogPtr theDialog, int item, char *format, ...)
  255. {
  256.     va_list args;
  257.     char pText[256];
  258.     int itemType;
  259.     Handle itemHandle;
  260.     Rect itemRect;
  261.  
  262.     va_start(args, format);
  263.     pText[0] = vsprintf(pText + 1, format, args);
  264.     va_end(args);
  265.     GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
  266.     SetIText(itemHandle, pText);
  267. }
  268.  
  269. static void dcontrol(DialogPtr theDialog, int item, int value)
  270. {
  271.     int itemType;
  272.     Handle itemHandle;
  273.     Rect itemRect;
  274.  
  275.     GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
  276.     SetCtlValue(itemHandle, value);
  277. }
  278.  
  279. static long int dgetint(DialogPtr theDialog, int item)
  280. {
  281.     char pText[256];
  282.     int itemType;
  283.     Handle itemHandle;
  284.     Rect itemRect;
  285.     long int num;
  286.  
  287.     GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
  288.     GetIText(itemHandle, pText);
  289.     StringToNum(pText, &num);
  290.     return (num);
  291. }
  292.  
  293. static void Signal(OSErr err)
  294. {
  295.     if (err != noErr)
  296.     {
  297.     aprintf("error %i occurred",err);
  298.     exit(1);
  299.     }
  300. }
  301.  
  302. static Byte Decimal2BCD(Byte n)
  303. {
  304.     return (((n / 10) << 4) + (n % 10));
  305. }
  306.  
  307. static Byte BCD2Decimal(Byte n)
  308. {
  309.     return (((n >> 4) * 10) + (n & 0x0f));
  310. }
  311.  
  312. static OSErr InitCD(void)
  313. {
  314.     return (OpenDriver("\p.AppleCD", &driverRefNum));
  315. }
  316.  
  317. static OSErr CDIO(CDIORec *cdIo)
  318. {
  319.     cdIo->ioCompletion = NULL;
  320.     cdIo->ioNamePtr = NULL;
  321.     cdIo->ioRefNum = ioRefNum;
  322.     return (PBControl((ParmBlkPtr) cdIo, false));
  323. }
  324.  
  325. static OSErr OpenCD(void)
  326. {
  327.     CDIORec whoIsThereRec;
  328.     OSErr err;
  329.  
  330.     whoIsThereRec.ioCompletion = NULL;
  331.     whoIsThereRec.ioNamePtr = NULL;
  332.     whoIsThereRec.ioRefNum = driverRefNum;
  333.     whoIsThereRec.csCode = csWhoIsThere;
  334.     if ((err = PBStatus((ParmBlkPtr) &whoIsThereRec, false)) != noErr)
  335.     {
  336.     return (err);
  337.     }
  338.     for (scsiID = 0; scsiID != 7; scsiID++)
  339.     {
  340.     /* Use lowest CD ID found */
  341.     if (whoIsThereRec.csParam.showIsThere.SCSIMask & (1 << scsiID))
  342.     {
  343.         ioRefNum = -(32 + scsiID) - 1;
  344.         return (noErr);
  345.     }
  346.     }
  347.     return (badUnitErr);
  348. }
  349.  
  350. /* Disc in Drive <=> discPresent */
  351. static OSErr DiscStatus(Boolean *discPresent)
  352. {
  353.     OSErr err;
  354.     CDIORec discStatusRec;
  355.  
  356.     discStatusRec.ioCompletion = NULL;
  357.     discStatusRec.ioNamePtr = NULL;
  358.     discStatusRec.ioRefNum = driverRefNum;
  359.     discStatusRec.csCode = csDiscStatus;
  360.     err = PBStatus((ParmBlkPtr) &discStatusRec, false);
  361.     *discPresent = discStatusRec.csParam.discStatus.discStatus == 1;
  362.     return (err);
  363. }
  364.  
  365. static OSErr DriveType(enum DriveTypes *driveType)
  366. {
  367.     CDIORec driveTypeRec;
  368.     OSErr err;
  369.  
  370.     driveTypeRec.ioCompletion = NULL;
  371.     driveTypeRec.ioNamePtr = NULL;
  372.     driveTypeRec.ioRefNum = driverRefNum;
  373.     driveTypeRec.csCode = csGetDriveType;
  374.     err = PBStatus((ParmBlkPtr) &driveTypeRec, false);
  375.     *driveType = driveTypeRec.csParam.driveType;
  376.     return (err);
  377. }
  378.  
  379. /* Gets number of tracks */
  380. static OSErr TrackCount(int *trackCount)
  381. {
  382.     OSErr err;
  383.     CDIORec readTOC;
  384.  
  385.     readTOC.csCode = csReadTOC;
  386.     readTOC.csParam.readTOC.subType = csReadTOCTrackCount;
  387.     if ((err = CDIO((CDIORec *) &readTOC)) == noErr)
  388.     {
  389.     *trackCount = BCD2Decimal(readTOC.csParam.readTOC.trackCount.lastTrackNumber) -
  390.               BCD2Decimal(readTOC.csParam.readTOC.trackCount.firstTrackNumber) + 1;
  391.     }
  392.     return (err);
  393. }
  394.  
  395. /* Info about total time */
  396. static OSErr TotalDiscTime(StartingAddress dTime)
  397. {
  398.     OSErr err;
  399.     CDIORec readTOC;
  400.  
  401.     readTOC.csCode = csReadTOC;
  402.     readTOC.csParam.readTOC.subType = csReadTOCLeadOut;
  403.     if ((err = CDIO(&readTOC)) == noErr)
  404.     {
  405.     dTime->minutes = BCD2Decimal(readTOC.csParam.readTOC.leadOut.minutes);
  406.     dTime->seconds = BCD2Decimal(readTOC.csParam.readTOC.leadOut.seconds);
  407.     dTime->frames = BCD2Decimal(readTOC.csParam.readTOC.leadOut.frames);
  408.     }
  409.     return (err);
  410. }
  411.  
  412. /* Info about track "track" */
  413. static OSErr TrackInfo(Byte track, StartingAddress dTime, int nrTracks)
  414. {
  415.     OSErr err;
  416.     CDIORec readTOC;
  417.     int i;
  418.  
  419.     readTOC.csCode = csReadTOC;
  420.     readTOC.csParam.readTOC.subType = csReadTOCStartingAddress;
  421.     readTOC.csParam.readTOC.startingAddress.buffer = dTime;
  422.     readTOC.csParam.readTOC.startingAddress.bufferSize = nrTracks * sizeof(struct StartingAddress);
  423.     readTOC.csParam.readTOC.startingAddress.startingTrackNumber = Decimal2BCD(track);
  424.     err = CDIO(&readTOC);
  425.     for (i = 0; i != nrTracks; i++)
  426.     {
  427.     dTime[i].minutes = BCD2Decimal(dTime[i].minutes);
  428.     dTime[i].seconds = BCD2Decimal(dTime[i].seconds);
  429.     dTime[i].frames = BCD2Decimal(dTime[i].frames);
  430.     }
  431.     return (err);
  432. }
  433.  
  434. static OSErr MediaCatalogNumber(Byte mcn[15])
  435. {
  436.     CDIORec mcnRec;
  437.     OSErr err;
  438.  
  439.     mcnRec.csCode = csReadMCN;
  440.     err = CDIO(&mcnRec);
  441.     memcpy(mcn, mcnRec.csParam.readMCN.mcn, 15);
  442.     return (err != noErr? err: (mcnRec.csParam.readMCN.mcValFound &0x80) == 0x80? noErr: -1);
  443. }
  444.  
  445. static OSErr InternationalStandardRecordingCode(Byte isrc[15])
  446. {
  447.     CDIORec isrcRec;
  448.     OSErr err;
  449.  
  450.     isrcRec.csCode = csReadISRC;
  451.     err = CDIO(&isrcRec);
  452.     memcpy(isrc, isrcRec.csParam.readISRC.isrc, 15);
  453.     return (err != noErr? err: (isrcRec.csParam.readISRC.tcValFound & 0x80) == 0x80? noErr: -1);
  454. }
  455.  
  456. static OSErr EjectCD(void)
  457. {
  458.     OSErr err;
  459.     Str255 ioName;
  460.     HVolumeParam pb;
  461.  
  462.     pb.ioCompletion = NULL;
  463.     pb.ioNamePtr = ioName;
  464.     pb.ioVolIndex = 0;
  465.     do
  466.     {
  467.     pb.ioVolIndex++;
  468.     if ((err = PBHGetVInfo((HParmBlkPtr) &pb, false)) != noErr)
  469.     {
  470.         return (err);
  471.     }
  472.     }
  473.     while (pb.ioVDRefNum != ioRefNum);
  474.     if ((err = PBEject((ParmBlkPtr) &pb)) == noErr)
  475.     {
  476.     err = PBUnmountVol((ParmBlkPtr) &pb);
  477.     }
  478.     return (err);
  479. }
  480.  
  481. static OSErr ReadAudio(void *data, long int bufSize, int minutes, int seconds, int frames)
  482. {
  483.     CDIORec audioRec;
  484.     OSErr err;
  485.  
  486.     audioRec.csCode = csReadAudio;
  487.     audioRec.csParam.readAudio.audioType = csReadAudioDataOnly;
  488.     audioRec.csParam.readAudio.buffer = data;
  489.     audioRec.csParam.readAudio.addressType = minSecFrameType;
  490.     audioRec.csParam.readAudio.address.startTime.controlField = 0;
  491.     audioRec.csParam.readAudio.address.startTime.minutes = Decimal2BCD(minutes);
  492.     audioRec.csParam.readAudio.address.startTime.seconds = Decimal2BCD(seconds);
  493.     audioRec.csParam.readAudio.address.startTime.frames = Decimal2BCD(frames);
  494.     audioRec.csParam.readAudio.numberOfFrames = bufSize / 2352;
  495.     memset(audioRec.csParam.readAudio.filler, 0, sizeof(audioRec.csParam.readAudio.filler));
  496.     return (CDIO(&audioRec));
  497. }
  498.  
  499. static void ReadTime(void)
  500. {
  501.     long int t0;
  502.     OSErr err;
  503.  
  504.     startFrames += tracks[startTrack - 1].frames;
  505.     startSeconds += startFrames / 75 + tracks[startTrack - 1].seconds;
  506.     startFrames %= 75;
  507.     startMinutes += startSeconds / 60 + tracks[startTrack - 1].minutes;
  508.     startSeconds %= 60;
  509.     endFrames += tracks[endTrack - 1].frames;
  510.     endSeconds += endFrames / 75 + tracks[endTrack - 1].seconds;
  511.     endFrames %= 75;
  512.     endMinutes += endSeconds / 60 + tracks[endTrack - 1].minutes;
  513.     endSeconds %= 60;
  514.  
  515.     bufSize = (((long int) (endMinutes - startMinutes) * 60 +
  516.         endSeconds - startSeconds) * 75
  517.            + endFrames - startFrames)
  518.           * 2352L;
  519.     numFrames = bufSize / 4;
  520.     if ((sndHandle = NewHandle(bufSize + headerSize)) == NULL)
  521.     {
  522.     aprintf("Not enough memory");
  523.     exit(1);
  524.     }
  525.     HLock(sndHandle);
  526.  
  527.     t0 = TickCount();
  528.     err = ReadAudio(*sndHandle + headerSize, bufSize, startMinutes, startSeconds, startFrames);
  529.     t0 = TickCount() - t0;
  530.     if (err != noErr)
  531.     {
  532.     Signal(err);
  533.     }
  534.     aprintf("%ld bytes read in %g sec.", bufSize, t0 / 60.0);
  535. }
  536.  
  537. static Boolean GetTimes(void)
  538. {
  539.     DialogPtr timeDialog = GetNewDialog(128, NULL, (char *) -1);
  540.     int item;
  541.     int itemType;
  542.     Handle itemHandle;
  543.     Rect itemRect;
  544.  
  545.     if (timeDialog == NULL)
  546.     {
  547.     aprintf("???");
  548.     ExitToShell();
  549.     }
  550.     SetPort(timeDialog);
  551.     dprintf(timeDialog, 3, "Number of tracks: %d", nrTracks);
  552.     dprintf(timeDialog, 4, "Total playing time: %d:%02d:%02d",
  553.                 totalTime.minutes, totalTime.seconds, totalTime.frames);
  554.     if (mcnErr == noErr) dprintf(timeDialog, 5, "MCN: %15s", mcn);
  555.     else if (mcnErr == -1) dprintf(timeDialog, 5, "MCN: unknown");
  556.     else dprintf(timeDialog, 5, "MCN: error %d", mcnErr);
  557.     if (isrcErr == noErr) dprintf(timeDialog, 6, "ISRC: %15s", isrc);
  558.     else if (isrcErr == -1) dprintf(timeDialog, 6, "ISRC: unknown");
  559.     else dprintf(timeDialog, 6, "ISRC: error %d", isrcErr);
  560.     dcontrol(timeDialog, 21, 1);
  561.     dcontrol(timeDialog, 23, 1);
  562.     dcontrol(timeDialog, 25, 1);
  563.     dcontrol(timeDialog, 28, 0);
  564.     GetDItem(timeDialog, 1, &itemType, &itemHandle, &itemRect);
  565.     PenSize(3, 3); InsetRect(&itemRect, -4, -4);
  566.     FrameRoundRect(&itemRect, 16, 16);
  567.     do
  568.     {
  569.     ModalDialog(NULL, &item);
  570.     if (item == 21 || item == 22)
  571.     {
  572.         channels = (item == 21? 1: 2);
  573.         dcontrol(timeDialog, 21, channels == 1);
  574.         dcontrol(timeDialog, 22, channels == 2);
  575.     }
  576.     else if (item == 23 || item == 24)
  577.     {
  578.         resolution = (item == 23? 8: 16);
  579.         dcontrol(timeDialog, 23, resolution == 8);
  580.         dcontrol(timeDialog, 24, resolution == 16);
  581.     }
  582.     else if (25 <= item && item <= 27)
  583.     {
  584.         sampleRate = (item == 25? 11025: (item == 26? 22050: 44100));
  585.         dcontrol(timeDialog, 25, sampleRate == 11025);
  586.         dcontrol(timeDialog, 26, sampleRate == 22050);
  587.         dcontrol(timeDialog, 27, sampleRate == 44100);
  588.     }
  589.     else if (item == 28)
  590.     {
  591.         normalize = !normalize;
  592.         dcontrol(timeDialog, 28, normalize);
  593.     }
  594.     }
  595.     while (item != 1 && item != 2);
  596.     startTrack = dgetint(timeDialog, 13);
  597.     startMinutes = dgetint(timeDialog, 14);
  598.     startSeconds = dgetint(timeDialog, 15);
  599.     startFrames = dgetint(timeDialog, 16);
  600.     endTrack = dgetint(timeDialog, 17);
  601.     endMinutes = dgetint(timeDialog, 18);
  602.     endSeconds = dgetint(timeDialog, 19);
  603.     endFrames = dgetint(timeDialog, 20);
  604.     if (channels == 1 && resolution == 8)
  605.     {
  606.     headerSize = sizeof(struct SndStruct) - sizeof(struct ExtHeader);
  607.     }
  608.     else
  609.     {
  610.     headerSize = sizeof(struct SndStruct);
  611.     }
  612.     DisposDialog(timeDialog);
  613.     return (item == 1);
  614. }
  615.  
  616. static long int MaxVal(void)
  617. {
  618.     long int length = bufSize;
  619.     IWrd *lChan = (IWrd *) (*sndHandle + headerSize);
  620.     IWrd *rChan = lChan + 1;
  621.     long int lDatum, rDatum;
  622.     int nrSamples = (sampleRate == 11025? 4: sampleRate == 22050? 2: 1);
  623.     int i;
  624.     Byte swap;
  625.     long int max = 0;
  626.  
  627.     while (length > 0)
  628.     {
  629.     lDatum = rDatum = 0;
  630.     for (i = 0; i != nrSamples; i++)
  631.     {
  632.         swap = lChan->lh[0]; lChan->lh[0] = lChan->lh[1]; lChan->lh[1] = swap;
  633.         swap = rChan->lh[0]; rChan->lh[0] = rChan->lh[1]; rChan->lh[1] = swap;
  634.         lDatum += lChan->word; rDatum += rChan->word;
  635.         lChan += 2; rChan += 2; length -= 4;
  636.     }
  637.     if (channels == 1)
  638.     {
  639.         lDatum = (lDatum + rDatum) / 2;
  640.         if (lDatum < 0) lDatum = -lDatum;
  641.         if (lDatum > max) max = lDatum;
  642.     }
  643.     else
  644.     {
  645.         if (lDatum < 0) lDatum = -lDatum;
  646.         if (lDatum > max) max = lDatum;
  647.         if (rDatum < 0) rDatum = -rDatum;
  648.         if (rDatum > max) max = rDatum;
  649.     }
  650.     }
  651.     return (max);
  652. }
  653.  
  654. static void ConvertSound(void)
  655. {
  656.     char *bPtr = *sndHandle + headerSize;
  657.     short int *wPtr = (short int *) bPtr;
  658.     long int length = bufSize;
  659.     IWrd *lChan = (IWrd *) bPtr;
  660.     IWrd *rChan = lChan + 1;
  661.     long int lDatum, rDatum;
  662.     int nrSamples = (sampleRate == 11025? 4: sampleRate == 22050? 2: 1);
  663.     int i;
  664.     Byte swap;
  665.     long int max = (normalize? MaxVal(): 32768);
  666.  
  667.     while (length > 0)
  668.     {
  669.     lDatum = rDatum = 0;
  670.     for (i = 0; i != nrSamples; i++)
  671.     {
  672.         if (!normalize)
  673.         {
  674.         swap = lChan->lh[0]; lChan->lh[0] = lChan->lh[1]; lChan->lh[1] = swap;
  675.         swap = rChan->lh[0]; rChan->lh[0] = rChan->lh[1]; rChan->lh[1] = swap;
  676.         }
  677.         lDatum += lChan->word; rDatum += rChan->word;
  678.         lChan += 2; rChan += 2; length -= 4;
  679.     }
  680.     if (normalize)
  681.     {
  682.         lDatum = lDatum * 32767 / max;
  683.         rDatum = rDatum * 32767 / max;
  684.     }
  685.     else
  686.     {
  687.         lDatum /= nrSamples;
  688.         rDatum /= nrSamples;
  689.     }
  690.     if (channels == 1)
  691.     {
  692.         lDatum = (lDatum + rDatum) / 2;
  693.     }
  694.     if (resolution == 8)
  695.     {
  696.         *bPtr++ = (Byte) (lDatum >> 8) ^ 0x80;
  697.     }
  698.     else
  699.     {
  700.         *wPtr++ = lDatum;
  701.     }
  702.     if (channels == 2)
  703.     {
  704.         if (resolution == 8)
  705.         {
  706.         *bPtr++ = (rDatum >> 8) ^ 0x80;
  707.         }
  708.         else
  709.         {
  710.         *wPtr++ = rDatum;
  711.         }
  712.     }
  713.     }
  714.     HUnlock(sndHandle);
  715.     SetHandleSize(sndHandle, headerSize + resolution / 8 * channels * numFrames);
  716.     Signal(ResError());
  717. }
  718.  
  719. static void InitHeader(void)
  720. {
  721.     long double dSampleRate = (double) sampleRate;
  722.  
  723.     numFrames = numFrames / (44100 / (long unsigned int) sampleRate);
  724.     genHeader.sampleRate = (long unsigned int) sampleRate << 16;
  725.     if (channels == 1 && resolution == 8)
  726.     {
  727.     genHeader.nrOfSamples = numFrames;
  728.     }
  729.     else
  730.     {
  731.     if (channels == 2)
  732.     {
  733.         genHeader.synthDef.synth[0].initCmd = initStereo;
  734.     }
  735.     genHeader.nrOfSamples = channels;
  736.     genHeader.encode = extSH;
  737.     genHeader.extHeader.numFrames = numFrames;
  738.     x96tox80(&dSampleRate, &genHeader.extHeader.AIFFSampleRate);
  739.     genHeader.extHeader.markerChunk = NULL;
  740.     genHeader.extHeader.instrumentChunks = NULL;
  741.     genHeader.extHeader.AESRecording = NULL;
  742.     genHeader.extHeader.sampleSize = resolution;
  743.     genHeader.extHeader.futureUse1 = 0;
  744.     genHeader.extHeader.futureUse2 = 0;
  745.     genHeader.extHeader.futureUse3 = 0;
  746.     genHeader.extHeader.futureUse4 = 0;
  747.     }
  748.     memcpy((void *) *sndHandle, &genHeader, headerSize);
  749. }
  750.  
  751. static void SaveSound(void)
  752. {
  753.     StandardFileReply reply;
  754.     int refNum;
  755.  
  756.     StandardPutFile("\pWhere to put file", "\pSound", &reply);
  757.     if (reply.sfGood)
  758.     {
  759.     if (reply.sfReplacing)
  760.     {
  761.         FSpDelete(&reply.sfFile);
  762.     }
  763.     FSpCreateResFile(&reply.sfFile, 'movr', 'sfil', smSystemScript);
  764.     Signal(ResError());
  765.     refNum = FSpOpenResFile(&reply.sfFile, fsWrPerm);
  766.     if (refNum == -1)
  767.     {
  768.         Signal(ResError());
  769.     }
  770.     AddResource(sndHandle, 'snd ', 128, reply.sfFile.name);
  771.     CloseResFile(refNum);
  772.     }
  773. }
  774.  
  775. static void SetupCD(void)
  776. {
  777.     /* Initialize CD */
  778.     Signal(InitCD());
  779.     Signal(OpenCD());
  780.  
  781.     /* Check for drive */
  782.     Signal(DriveType(&driveType));
  783.     if (driveType < AppleCD300)
  784.     {
  785.     aprintf("Improper drive");
  786.     }
  787.  
  788.     /* Check if present and of proper type */
  789.     Signal(DiscStatus(&discPresent));
  790.     if (!discPresent)
  791.     {
  792.     aprintf("No disc present");
  793.     ExitToShell();
  794.     }
  795.  
  796.     /* Get CD info */
  797.     Signal(TrackCount(&nrTracks));
  798.     Signal(TotalDiscTime(&totalTime));
  799.  
  800.     /* Get info of all tracks */
  801.     Signal(TrackInfo(1, tracks, nrTracks));
  802.  
  803.     /* Check for Media Catalog Number */
  804.     mcnErr = MediaCatalogNumber(mcn);
  805.  
  806.     /* Check for International Standard Recording Code */
  807.     isrcErr = InternationalStandardRecordingCode(isrc);
  808. }
  809.  
  810. void main(void)
  811. {
  812.     InitGraf(&thePort);
  813.     InitFonts();
  814.     FlushEvents(everyEvent, 0);
  815.     InitWindows();
  816.     InitMenus();
  817.     TEInit();
  818.     InitDialogs(NULL);
  819.     InitCursor();
  820.  
  821.     SetupCD();
  822.     if (GetTimes())
  823.     {
  824.     ReadTime();
  825.     InitHeader();
  826.     ConvertSound();
  827.     SaveSound();
  828.     }
  829. }
  830.  
  831. /* ----------------------------------------------------------------- */
  832. /* ReadAudioFromCD.r: compile with SARez */
  833.  
  834. data 'ALRT' (128) {
  835.     $"0048 0034 00A7 0123 0080 5555 300A"
  836. };
  837.  
  838. data 'DITL' (128) {
  839.     $"0002 0000 0000 003C 005A 0050 0094 0404"
  840.     $"416C 6173 0000 0000 000A 000A 002A 002A"
  841.     $"A002 0000 0000 0000 000A 0032 002A 00DC"
  842.     $"8802 5E30"
  843. };
  844.  
  845. data 'DITL' (129) {
  846.     $"001B 0000 0000 0057 0118 006B 0152 0402"
  847.     $"4F6B 0000 0000 0076 0118 008A 0152 0406"
  848.     $"4361 6E63 656C 0000 0000 000A 000A 001A"
  849.     $"0098 8800 0000 0000 000A 00A0 001A 015B"
  850.     $"8800 0000 0000 001E 000A 002D 0131 8800"
  851.     $"0000 0000 0032 000A 0041 0131 8800 0000"
  852.     $"0000 005A 000A 006A 0031 8804 4672 6F6D"
  853.     $"0000 0000 0046 0032 0056 0058 8805 5472"
  854.     $"6163 6B00 0000 0000 0077 000A 0087 0022"
  855.     $"8802 546F 0000 0000 0046 0064 0056 0096"
  856.     $"8806 4D69 6E75 7465 0000 0000 0046 009B"
  857.     $"0056 00CF 8806 5365 636F 6E64 0000 0000"
  858.     $"0046 00D2 0056 0102 8805 4672 616D 6500"
  859.     $"0000 0000 005A 0033 006B 005A 1000 0000"
  860.     $"0000 005A 0069 006B 0090 1000 0000 0000"
  861.     $"005A 00A0 006B 00C7 1000 0000 0000 005A"
  862.     $"00D5 006B 00FC 1000 0000 0000 0076 0033"
  863.     $"0087 005A 1000 0000 0000 0076 0069 0087"
  864.     $"0090 1000 0000 0000 0076 00A0 0087 00C7"
  865.     $"1000 0000 0000 0076 00D5 0087 00FC 1000"
  866.     $"0000 0000 0096 000A 00A8 0048 0604 4D6F"
  867.     $"6E6F 0000 0000 00AA 000A 00BC 004C 0606"
  868.     $"5374 6572 656F 0000 0000 0096 0064 00A8"
  869.     $"0098 0605 3820 6269 7400 0000 0000 00AA"
  870.     $"0064 00BD 00A0 0606 3136 2062 6974 0000"
  871.     $"0000 0096 00B4 00A8 00FC 0606 3131 206B"
  872.     $"487A 0000 0000 00AA 00B4 00BC 00FC 0606"
  873.     $"3232 206B 487A 0000 0000 00BE 00B4 00D0"
  874.     $"00FC 0606 3434 206B 487A 0000 0000 00BE"
  875.     $"000A 00D0 0074 0509 4E6F 726D 616C 697A"
  876.     $"6500"
  877. };
  878.  
  879. data 'DLOG' (128) {
  880.     $"003C 0020 0116 017D 0005 0100 0000 0000"
  881.     $"0000 0081 00"
  882. };
  883.  
  884. /* ----------------------------------------------------------------- */
  885. /* older SCSI parts */
  886.  
  887. /*From the CDROM FAQ:
  888.   There are several ways to read digital audio from Sony CDU 561
  889.   and Sony CDU 8003 mechanisms. Note that the technique of merely
  890.   setting the density (0x82) using MODE SELECT SCSI command as on
  891.   Toshiba 3401s will not work.
  892.  
  893.   Here are three ways to read digital audio Red Book standard
  894.   audio track data across the SCSI bus into your computer complete
  895.   with all sound processing already performed (For example the
  896.   CIRC routine already run and the output is LRLRLR pairs of 16
  897.   bit digital audio samples 2352 bytes per CD-ROM block.
  898.  
  899.   Method 1 : READ CD-DA scsi command 0xD8
  900.  
  901.   Byte 0: D8
  902.   1: <LUN stuff> 0
  903.   2: <4th most significant byte of logical block address>
  904.   3: <3rd byte>
  905.   4: <2nd>
  906.   5: <1st, lowest of the address>
  907.   6: <4th most significant byte of transfer length
  908.   7: <3rd byte>
  909.   8: <2nd>
  910.   9: <1st, lowest of the number of contiguos blocks to transfer>
  911.   10: <special sub code selector> (0 == normal 2352, other values
  912.       are 01, 02, 03)
  913.   11: <control>
  914.    
  915.   Method 2 : READ CD-DA MSF scsi command 0xD9
  916.    
  917.   byte 0: D9
  918.   1: <LUN stuff> 0
  919.   2: 0
  920.   3: <starting minute in binary not BCD>
  921.   4: <starting second in binary not BCD>
  922.   5: <starting frame (75th of a second) in binary not BCD>
  923.   6: 0
  924.   7: <ending minute in binary not BCD>
  925.   8: <ending second in binary not BCD>
  926.   9: <ending frame (75th of a second) in binary not BCD>
  927.   10: <special sub code selector> (0 == normal 2352 each, other
  928.       values are 01, 02, 03)
  929.   11: <control>
  930.    
  931.   For this one you will need to remember how to convert MSF to
  932.   logical (LBA) address to set the SCSI transfer length correctly
  933.   to avoid the Mac SCSI manager reporting a phase error. to
  934.   calculate the number of bytes total you will get use the formula:
  935.  
  936.   ((Me-Ms)*60*75 + (Se-Ss) * 75 + (Fe-Fs)) * (2352) */
  937.  
  938. #include <stdio.h>
  939. #include <stdlib.h>
  940. #include <string.h>
  941. #include <SCSI.h>
  942.  
  943. #define csDiscStatus 8
  944. #define csWhoIsThere 97
  945. #define csReadTOC 100
  946. #define csReadTOCTrackCount 1
  947. #define csReadTOCEndOfDisk 2
  948. #define csReadTOCStartTime 2
  949. #define csReadQ 101
  950.  
  951. #define scsiReadXfer 1
  952. #define scsiWriteXfer -1
  953. #define scsiNoXfer 0
  954.  
  955. #define kSCSIStatusMask 0x1f
  956.  
  957. #define kGood 0x00
  958. #define kCheckCondition 0x02
  959. #define kBusy 0x08
  960. #define kReservationConflict 0x18
  961.  
  962. #define kSCSIRetryValue 5
  963.  
  964. #define bLogicalUnitLSB 5
  965.  
  966. #define kCommandComplete 0x00
  967. #define kExtendedMessage 0x01
  968. #define kSaveDataPointer 0x02
  969. #define kRestorePointers 0x03
  970. #define kDisconnect 0x04
  971. #define kMessageReject 0x07
  972. #define kLinkedCommandComplete 0x0a
  973. #define kLinkedCommandCompleteWithFlags 0x0b
  974.  
  975. #define DoSCSISenseUnit(TargetSCSIID, LogicalUnit) -1 /*
  976. FindError(TargetSCSIID) */
  977.  
  978. enum
  979. {
  980.     eSCSIReservationConflict = 0xf000,
  981.     eSCSIBusy,
  982.     eDisconnect,
  983.     eMessageReject,
  984.     eReplyTOErr
  985. };
  986.  
  987. static struct
  988. {
  989.     Byte command; /* D9 */
  990.     Byte lun; /* 0 */
  991.     Byte zero1; /* 0 */
  992.     Byte startMinute; /* Not in BCD */
  993.     Byte startSecond;
  994.     Byte startFrame;
  995.     Byte zero2; /* 0 */
  996.     Byte endMinute; /* Not in BCD */
  997.     Byte endSecond;
  998.     Byte endFrame;
  999.     Byte subCodeSelector; /* 0 == normal 2352 each, other values are 01, 02, 03 */
  1000.     Byte control; /* ??? */
  1001. } readCommand1 =
  1002. {
  1003.     0xD9, 0x00, 0x00,
  1004.     0x01, 0x02, 0x03,
  1005.     0x00, 0x01, 0x02,
  1006.     0x04, 0x00, 0x00
  1007. };
  1008.  
  1009. static OSErr DoSCSIOutput(short TargetSCSIID, short scsiCmdSize, void *scsiCmd,
  1010.               SCSIInstr *scsiInst, short RdWrNone, long TimeOut)
  1011. {
  1012.     short ReturnedSCSIStat;
  1013.     short ReturnedSCSIMessage;
  1014.     short NumTries = 0;
  1015.     short stat;
  1016.     OSErr err;
  1017.     OSErr err1;
  1018.     short i;
  1019.     short message;
  1020.     
  1021.     while (NumTries++ < kSCSIRetryValue) 
  1022.     {
  1023.     err = SCSIGet();
  1024.     if (err == noErr) 
  1025.     {
  1026.         err = SCSISelect(TargetSCSIID);
  1027.         if (err == noErr) 
  1028.         {
  1029.         err = SCSICmd(scsiCmd, scsiCmdSize);
  1030.         if (err == noErr) 
  1031.         {
  1032.             stat = SCSIStat();
  1033.             switch (RdWrNone) 
  1034.             {
  1035.             case scsiReadXfer:
  1036.                 err = SCSIRead((Ptr) scsiInst);
  1037.                 break;
  1038.             case scsiWriteXfer:
  1039.                 err = SCSIWrite((Ptr) scsiInst);
  1040.                 break;
  1041.             }
  1042.         }
  1043.         err1 = SCSIComplete(&ReturnedSCSIStat, &ReturnedSCSIMessage, TimeOut);
  1044.         if (err == noErr) 
  1045.         {
  1046.             if (err1 == noErr) 
  1047.             {
  1048.             switch (ReturnedSCSIStat & kSCSIStatusMask) 
  1049.             {
  1050.                 case kGood:
  1051.                 err = noErr;
  1052.                 break;
  1053.              /* case kConditionMetGood:
  1054.                 err = noErr;
  1055.                 break;
  1056.                 case kIntermediateGood:
  1057.                 err = noErr;
  1058.                 break;
  1059.                 case kIntermediateMetGood:
  1060.                 err = noErr;
  1061.                 break; */
  1062.                 case kReservationConflict:
  1063.                 err = eSCSIReservationConflict;
  1064.                 break;
  1065.                 case kBusy:
  1066.                 err = eSCSIBusy;
  1067.                 break;
  1068.                 case kCheckCondition:
  1069.                 switch (ReturnedSCSIMessage) 
  1070.                 {
  1071.                     case kCommandComplete:
  1072.                     case kLinkedCommandCompleteWithFlags:
  1073.                     case kLinkedCommandComplete:
  1074.                     err = DoSCSISenseUnit(TargetSCSIID,
  1075.                                   scsiCmd->LUN_Res >> bLogicalUnitLSB);
  1076.                     break;
  1077.                     case kExtendedMessage:
  1078.                     if (SCSIMsgIn(&message) == noErr) 
  1079.                     {
  1080.                         for (i = 0; i < message; i++) 
  1081.                         {
  1082.                         if (SCSIMsgIn(&message) != noErr)
  1083.                         {
  1084.                             break;
  1085.                         }
  1086.                         }
  1087.                     }
  1088.                     break;
  1089.                     case kDisconnect:
  1090.                     err = eDisconnect;
  1091.                     break;
  1092.                     case kMessageReject:
  1093.                     err = eMessageReject;
  1094.                     break;
  1095.                     default:
  1096.                     if (ReturnedSCSIMessage >= 0x80) 
  1097.                     {
  1098.                         err = noErr;
  1099.                     } 
  1100.                     else 
  1101.                     {
  1102.                         err = DoSCSISenseUnit(TargetSCSIID,
  1103.                                   scsiCmd->LUN_Res >> bLogicalUnitLSB);
  1104.                     }
  1105.                     break;
  1106.                 }
  1107.                 break;
  1108.             }
  1109.             } 
  1110.             else 
  1111.             {
  1112.             err = err1;
  1113.             }
  1114.         }
  1115.         break;
  1116.         }
  1117.     }
  1118.     }
  1119.     if (NumTries >= kSCSIRetryValue) 
  1120.     {
  1121.     err = eReplyTOErr;
  1122.     }
  1123.     return (err);
  1124. }
  1125.  
  1126. static void ReadTime(void)
  1127. {
  1128.     long int t0;
  1129.     int track, startTrack, startMinutes, startSeconds, startFrames,
  1130.            endTrack,   endMinutes,   endSeconds,   endFrames;
  1131.     OSErr err;
  1132.  
  1133.     /* Enter format as track+min:sec:frame */
  1134.     printf("from? ");
  1135.     switch (scanf("%d+%d:%d:%d", &startTrack, &startMinutes, &startSeconds, &startFrames))
  1136.     {
  1137.     case 0:
  1138.         startTrack = 1;
  1139.     case 1:
  1140.         startMinutes = 0;
  1141.     case 2:
  1142.         startSeconds = 0;
  1143.     case 3:
  1144.         startFrames = 0;
  1145.     }
  1146.     printf("to? ");
  1147.     switch (scanf("%d+%d:%d:%d", &endTrack, &endMinutes, &endSeconds, &endFrames))
  1148.     {
  1149.     case 0:
  1150.         endTrack = 1;
  1151.     case 1:
  1152.         endMinutes = 0;
  1153.     case 2:
  1154.         endSeconds = 0;
  1155.     case 3:
  1156.         endFrames = 0;
  1157.     }
  1158.  
  1159.     startFrames += tracks[startTrack].frame;
  1160.     if (startFrames >= 75)
  1161.     {
  1162.     startSeconds += startFrames / 75;
  1163.     startFrames %= 75;
  1164.     }
  1165.     startSeconds += tracks[startTrack].second;
  1166.     if (startSeconds >= 60)
  1167.     {
  1168.     startMinutes += startSeconds / 60;
  1169.     startSeconds %= 60;
  1170.     }
  1171.     startMinutes += tracks[startTrack].minute;
  1172.     endFrames += tracks[endTrack].frame;
  1173.     if (endFrames >= 75)
  1174.     {
  1175.     endSeconds += endFrames / 75;
  1176.     endFrames %= 75;
  1177.     }
  1178.     endSeconds += tracks[endTrack].second;
  1179.     if (endSeconds >= 60)
  1180.     {
  1181.     endMinutes += endSeconds / 60;
  1182.     endSeconds %= 60;
  1183.     }
  1184.     endMinutes += tracks[endTrack].minute;
  1185.  
  1186.     bufSize = 2352L * (long int) ((long int) ((long int)
  1187.           (endMinutes - startMinutes) * 60 + endSeconds - startSeconds)
  1188.           * 75 + endFrames - startFrames);
  1189.     if ((data = malloc(bufSize)) == NULL)
  1190.     {
  1191.     perror("data");
  1192.     exit(1);
  1193.     }
  1194.  
  1195.     tib[0].scParam1 = (long int) data;
  1196.     tib[0].scParam2 = bufSize;
  1197.     readCommand1.startMinute = startMinutes;
  1198.     readCommand1.startSecond = startSeconds;
  1199.     readCommand1.startFrame = startFrames;
  1200.     readCommand1.endMinute = endMinutes;
  1201.     readCommand1.endSecond = endSeconds;
  1202.     readCommand1.endFrame = endFrames;
  1203.     t0 = TickCount();
  1204.     err = DoSCSIOutput(scsiID, sizeof(readCommand1), &readCommand1,
  1205.                tib, scsiReadXfer, 60 + bufSize / 2940);
  1206.     t0 = TickCount() - t0;
  1207.     if (err != noErr)
  1208.     {
  1209.     Signal(err);
  1210.     }
  1211.     printf("%ld bytes read in %g sec.\n", bufSize, t0 / 60.0);
  1212.     DumpBytes1(data, bufSize); /* Do something with it... */
  1213. }
  1214.